package com.wangzhenyu.learn.test.android_.storage_

import android.Manifest
import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.wangzhenyu.learn.R
import com.wangzhenyu.common.util.log.LogUtil
import java.io.*


//文件系统
//应用专属存储空间：
//  存储仅供应用使用的文件，可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。使用内部存储空间中的目录保存其他应用不应访问的敏感信息。
//  应用专属文件
//  访问方法：从内部存储空间访问，可以使用 getFilesDir() 或 getCacheDir() 方法  从外部存储空间访问，可以使用 getExternalFilesDir() 或 getExternalCacheDir() 方法
//  所需权限：从内部存储空间访问不需要任何权限；如果应用在搭载 Android 4.4（API 级别 19）或更高版本的设备上运行，从外部存储空间访问不需要任何权限
//  其他应用不可访问	卸载应用时会移除文件

//共享存储：
//  存储您的应用打算与其他应用共享的文件，包括媒体、文档和其他文件。
//  媒体：
//  访问方法：MediaStore API
//  所需权限：在 Android 11（API 级别 30）或更高版本中，访问其他应用的文件需要 READ_EXTERNAL_STORAGE
//           在 Android 10（API 级别 29）中，访问其他应用的文件需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE
//           在 Android 9（API 级别 28）或更低版本中，访问所有文件均需要相关权限
//  其他应用访问需要 READ_EXTERNAL_STORAGE 权限  卸载应用时不会移除文件
//  文档和其他文件：
//  访问方法：存储访问框架
//  无需权限
//  其他应用访问可以通过系统文件选择器访问  卸载应用时不会移除文件

//偏好设置：
//  以键值对形式存储私有原始数据。
//  应用偏好设置：
//  访问方法：Jetpack Preferences 库
//  无需权限
//  其他应用不可访问	卸载应用时会移除文件

//数据库：
//  使用 Room 持久性库将结构化数据存储在专用数据库中。
//  访问方法：Room 持久性库
//  无需权限
//  其他应用不可访问	卸载应用时会移除文件

//  内部存储空间目录：
//  这些目录既包括用于存储持久性文件的专属位置，也包括用于存储缓存数据的其他位置。系统会阻止其他应用访问这些位置，并且在 Android 10（API 级别 29）及更高版本中，系统会对这些位置进行加密。
//  这些特征使得这些位置非常适合存储只有应用本身才能访问的敏感数据。
//  外部存储空间目录：
//  这些目录既包括用于存储持久性文件的专属位置，也包括用于存储缓存数据的其他位置。虽然其他应用可以在具有适当权限的情况下访问这些目录，但存储在这些目录中的文件仅供您的应用使用。
//  如果您明确打算创建其他应用能够访问的文件，您的应用应改为将这些文件存储在外部存储空间的共享存储空间部分。


//查看图片
//  R.drawable.doc001
@Suppress("UNUSED")
object InstanceOfStorage {

    //内部存储空间文件存储路径
    fun util01(context: Context) {
        //   /data/user/0/com.wangzhenyu.learn/files
        LogUtil.default01(context.filesDir.toString())
    }

    //在您的内部存储空间内创建目录
    //创建出来的目录前面会加一个app_
    //通过绝对路径对这个目录操作似乎不起作用，建议使用getDir对目录进行处理，getDir返回的是一个File文件
    fun util02(context: Context) {
        val temp = context.getDir("wzy", Context.MODE_PRIVATE)
        LogUtil.default01(temp.absolutePath)
    }

    //删除/data/user/0/com.xxx.xxx/files目录中对应test的文件，如果存在并删除成功，返回true；反之返回false
    fun util03(context: Context) {

        val file = File(context.filesDir, "test.txt")

        if (file.exists()) {
            LogUtil.default01("这个存在")
            file.delete()
            LogUtil.success()
        } else {
            LogUtil.default01("这个不存在")
            LogUtil.fail()
        }

    }

    //列出 /data/user/0/com.xxx.xxx/files 目录下的所有的文件
    fun util04(context: Context) {

        val list = context.fileList()
        for (i in list) {
            LogUtil.default01(i)
        }

    }

    //由于外部存储空间位于用户可能能够移除的物理卷上，因此在尝试从外部存储空间读取应用专属数据或将应用专属数据写入外部存储空间之前，请验证该卷是否可访问
    fun util05(): Boolean {
        return Environment.getExternalStorageState() in setOf(
            Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY
        )
    }

    //访问共享存储，申请权限
    fun util06(register: ActivityResultLauncher<Array<String>>) {
        //Android 6.0 之前是无需申请动态权限的，在AndroidManifest.xml 里声明存储权限
        //<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        //<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

        //Android 6.0 后需要动态申请权限，除了在AndroidManifest.xml 里声明存储权限外，还需要在代码里动态申请

        //API28以及以下，需要两个权限，否则只需要READ_EXTERNAL_STORAGE
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            register.launch(
                arrayOf(
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                )
            )
        } else {
            register.launch(
                arrayOf(
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                )
            )
        }

    }

    //保存到应用专属存储空间
    //从内部存储空间访问
    //写入
    //位置为 /data/data/项目包名/files/
    //跟上面那个/data/user/0/com.wangzhenyu.learn/files是一样的
    fun test01(context: Context) {

        val fileName = "myFile"
        val fileContents = """
            I am Your Father!
            I am Your Daddy!
            I am WZY!
        """.trimIndent()

        context.openFileOutput(fileName, Context.MODE_PRIVATE).buffered().use {
            it.write(fileContents.toByteArray())
        }
    }

    //读取
    fun test02(context: Context) {
        context.openFileInput("myFile").bufferedReader().useLines {
            LogUtil.default01(it.fold("") { some, text ->
                "$some\n$text"
            })
        }
    }

    //注意：此缓存目录旨在存储应用的少量敏感数据。如需确定应用当前可用的缓存空间大小，请调用 getCacheQuotaBytes()。
    //写入缓存
    //当设备的内部存储空间不足时，Android 可能会删除这些缓存文件以回收空间。因此，请在读取前检查缓存文件是否存在。
    fun test03(context: Context) {
        val fileName = "cacheInstance"
        val content = """
            I am Your Father!
            I am Your Daddy!
            I am WZY!
            Hi Hi Hi Hello
        """.trimIndent()
        val file = File(context.cacheDir, fileName)
        if (!file.exists()) File.createTempFile(fileName, null, context.cacheDir)

        BufferedOutputStream(FileOutputStream(file)).use {
            it.write(content.toByteArray())
        }
    }

    //移除缓存缓存
    fun test04(context: Context) {
        val fileName = "cacheInstance"
        val file = File(context.cacheDir, fileName)
        if (file.exists()) file.delete()
    }

    //从外部存储空间访问
    //获取外部存储空间位置
    //分配内部存储分区作为外部存储空间的设备也会提供 SD 卡插槽。这意味着设备具有多个可能包含外部存储空间的物理卷，因此您需要选择用于应用专属存储空间的物理卷。
    //如需访问其他位置，请调用 ContextCompat.getExternalFilesDirs()。如代码段中所示，返回数组中的第一个元素被视为主外部存储卷。除非该卷已满或不可用，否则请使用该卷。
    fun test05(context: Context) {
        val externalStorageVolumes: Array<out File> =
            ContextCompat.getExternalFilesDirs(context, null)
        val primaryExternalStorage = externalStorageVolumes[0]
        LogUtil.default01(primaryExternalStorage.path)
    }

    //从外部存储设备访问应用专用文件
    fun test06(context: Context) {
        val fileName = "myFile"
        val appSpecificExternalDir = File(context.getExternalFilesDir(null), fileName)
        if (!appSpecificExternalDir.exists()) appSpecificExternalDir.createNewFile()
        LogUtil.assess {
            appSpecificExternalDir.exists()
        }
        LogUtil.default01(appSpecificExternalDir.absolutePath)
    }

    //将应用专属文件添加到外部存储空间中的缓存，
    fun test07(context: Context) {
        val filename = "myCacheFile"
        val externalCacheFile = File(context.externalCacheDir, filename)
        LogUtil.assess {
            externalCacheFile.exists()
        }
        if (!externalCacheFile.exists()) externalCacheFile.createNewFile()
        LogUtil.assess {
            externalCacheFile.exists()
        }
        LogUtil.default01(externalCacheFile.path)
    }

    //从外部缓存目录中移除文件
    fun test08(context: Context) {
        val filename = "myCacheFile"
        val externalCacheFile = File(context.externalCacheDir, filename)
        if (externalCacheFile.exists()) externalCacheFile.delete()
    }

    //如果应用支持使用仅在我的应用内对用户有价值的媒体文件，最好将这些文件存储在外部存储空间中的应用专属目录中
    //scale 改变...的大小
    fun test09(context: Context) {

        val fileName = "zr.jpg"
        val file = File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName)

        if (!file.exists()) file.createNewFile()
        LogUtil.default01(file.absolutePath)
        //设置缩放为false
        val option = BitmapFactory.Options()
        option.inScaled = false
        val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.zr, option)
        var bufferedOutputStream: BufferedOutputStream? = null

        try {
            bufferedOutputStream = BufferedOutputStream(FileOutputStream(file))
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bufferedOutputStream)
        } catch (e: Exception) {
            LogUtil.default02(e.message.toString())
        } finally {
            bufferedOutputStream?.close()
            LogUtil.default01("OK")
        }
    }

    //共享存储
    //媒体内容：系统提供标准的公共目录来存储这些类型的文件，这样用户就可以将所有照片保存在一个公共位置，将所有音乐和音频文件保存在另一个公共位置，
    // 依此类推。您的应用可以使用此平台的 MediaStore API 访问此内容。
    //文档和其他文件：系统有一个特殊目录，用于包含其他文件类型，例如 PDF 文档和采用 EPUB 格式的图书。您的应用可以使用此平台的存储访问框架访问这些文件。

    //访问共享存储需要权限 见util06()
    //访问媒体文件
    //直接构造路径获取图片测试一
    fun test10(): Bitmap {
        //获取目录：/storage/emulated/0
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath =
            rootFile.absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "1012.jpg"
        LogUtil.default01(imagePath)

        return BitmapFactory.decodeFile(imagePath)
    }

    //MediaStore的使用
    /*  Uri
    Image
    Uri uri1 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
    Uri uri2 = MediaStore.Images.Media.getContentUri("external");
    Uri uri3 = Uri.parse("content://media/external/images/media");
    Video
    Uri uri1 = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
    Uri uri2 = MediaStore.Video.Media.getContentUri("external");
    Uri uri3 = Uri.parse("content://media/external/video/media");
    File
    Uri uri1 = MediaStore.Files.getContentUri("external");
    Uri uri2 = Uri.parse("content://media/external/files");
    Audio
    Uri uri1 = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    Uri uri2 = MediaStore.Audio.Media.getContentUri("external");
    Uri uri3 = Uri.parse("content://media/external/audio/media");
    Download
    Uri uri1 = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
    Uri uri2 = MediaStore.Downloads.getContentUri("external");
    ImageFromFile
    Uri uri1 = MediaStore.Files.getContentUri("external");
    Uri uri2 = Uri.parse("content://media/external/files");*/
    //使用MediaStore获取图片测试二
    fun test11(context: Context): List<Bitmap> {
        val bitmapList = mutableListOf<Bitmap>()
        val contentResolver = context.contentResolver
        val cursor = contentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null
        )
        cursor?.apply {

            LogUtil.default01("===========================")
            LogUtil.default01(columnCount.toString())
            for (i in columnNames) {
                LogUtil.default01(i)
            }
            LogUtil.default01(count.toString())

            while (cursor.moveToNext()) {
                val imagePath =
                    getString(getColumnIndexOrThrow(MediaStore.Images.ImageColumns.DATA))
                LogUtil.default01(imagePath, "WZY1Path")
                bitmapList.add(BitmapFactory.decodeFile(imagePath))
            }
        }
        cursor?.close()
        return bitmapList
    }

    //在sdcard下创建文件夹，并保存图片
    //API高于28,无法在/sdcard/目录下创建文件夹和文件
    fun test12() {
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath = rootFile.absolutePath + File.separator + "WZYHello"
        LogUtil.default01(imagePath)


        val file = File(imagePath)
        if (!file.exists()) {
            LogUtil.default01("文件不存在")
            file.mkdirs()
        } else {
            LogUtil.default01("文件存在")
        }


    }

    //在Pictures下创建文件夹，并保存图片
    //使用decodeResource(不建议使用这个)
    fun test13(context: Context) {
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath =
            rootFile.absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "WZYPictures"

        val file = File(imagePath)
        if (!file.exists()) file.mkdirs()

        val pictureFile = File(file, "sun01.png")
        if (!pictureFile.exists()) pictureFile.createNewFile()

        val option = BitmapFactory.Options()
        option.inScaled = false
        val bitmap = BitmapFactory.decodeResource(context.resources, R.drawable.sun02, option)
        var bufferedOutputStream: BufferedOutputStream? = null

        try {
            bufferedOutputStream = BufferedOutputStream(FileOutputStream(pictureFile))
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bufferedOutputStream)
            //需要要让图库知道新增了图片

            LogUtil.default02(pictureFile.path)
            MediaScannerConnection.scanFile(context, arrayOf(file.path), null, null)
//            MediaScannerConnection.scanFile(context, arrayOf(file.toString()),
//                 null, null)
        } catch (e: Exception) {
            LogUtil.default02(e.message.toString())
        } finally {
            bufferedOutputStream?.close()
            LogUtil.default01("OK")
        }

    }

    //在Pictures下创建文件夹，并保存图片
    //使用decodeStream
    fun test14(context: Context) {
        val rootFile = Environment.getExternalStorageDirectory()
        val imagePath =
            rootFile.absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "WZYPictures"
        LogUtil.default01(imagePath)

        val file = File(imagePath)
        if (!file.exists()) {
            file.mkdirs()
        }
        val pictureFile = File(file, "sun01.jpg")
        if (!pictureFile.exists()) pictureFile.createNewFile()

        val bitmap = BitmapFactory.decodeStream(context.resources.openRawResource(R.raw.sun01))
        LogUtil.default01(bitmap.config.toString())
        var bufferedOutputStream: BufferedOutputStream? = null

        try {
            bufferedOutputStream = BufferedOutputStream(FileOutputStream(pictureFile))
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bufferedOutputStream)
        } catch (e: Exception) {
            LogUtil.default02(e.message.toString())
        } finally {
            bufferedOutputStream?.close()
            LogUtil.default01("OK")
        }
    }

    //通过SAF访问文档和其它文件
    //Storage Access Framework 简称SAF：存储访问框架。相当于系统内置了文件选择器，通过它可以拿到想要访问的文件信息。
    //先要在onCreate中声明变量register，赋值为test15(this)
    //在使用的地方调用register.launch("Hello")  "这个Hello没有什么意义"
    fun test15(activity: AppCompatActivity): ActivityResultLauncher<String> {

        return activity.registerForActivityResult(SAFActivityResult()) {
            //处理Uri
            LogUtil.default01(it?.path.toString())
        }
        //val inputStream = contentResolver.openInputStream(it)
        //val bitmap = BitmapFactory.decodeStream(inputStream)
    }

    //通过Uri插入图片
    fun test16(context: Context): Boolean {
        val fileName = "sun03.jpg"
        val contentValues = ContentValues()
        contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            contentValues.put(
                MediaStore.Images.ImageColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
            )
        } else {
            //实际上不起作用，建议使用传统方法
            contentValues.put(
                MediaStore.Images.ImageColumns.DATA,
                Environment.getExternalStorageState() + File.separator + Environment.DIRECTORY_PICTURES + File.separator + fileName
            )
        }
        val uri = context.contentResolver.insert(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues
        ) ?: return false

        var outputStream: OutputStream? = null
        return try {
            val bitmap = BitmapFactory.decodeStream(context.resources.openRawResource(R.raw.sun03))
            outputStream = context.contentResolver.openOutputStream(uri)
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
        } catch (e: Exception) {
            LogUtil.default01(e.message.toString())
            false
        } finally {
            outputStream?.close()
        }
    }

    //MediaStore现代操作
    //测试环境为API31 和 API28
    //增加图片
    fun addPicture(context: Context) {

        //文件名和路径
        val fileName = "sun06.jpg"
        val fileAbsolutePath =
            Environment.getExternalStorageDirectory().absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "WZYPictures"
        val openOutputStream: OutputStream?
        val uri: Uri?

        LogUtil.default01(fileAbsolutePath)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) {
            //开启了分区存储
            val contentValues = ContentValues()
            contentValues.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileName)
            contentValues.put(
                MediaStore.Images.ImageColumns.RELATIVE_PATH,
                Environment.DIRECTORY_PICTURES + "/WZYPictures"
            )
            //开始插入
            uri = context.contentResolver.insert(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                contentValues
            ) ?: return
            openOutputStream = context.contentResolver.openOutputStream(uri)
        } else {
            //未开启分区存储，使用传统方式
            File(fileAbsolutePath).apply {
                if (!exists()) mkdirs()
            }
            openOutputStream = FileOutputStream(fileAbsolutePath + File.separator + fileName)
        }

        try {
            val bitmap = BitmapFactory.decodeStream(context.resources.openRawResource(R.raw.sun06))
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, openOutputStream)
        } catch (e: Exception) {
            LogUtil.default01(e.message.toString())
        } finally {
            openOutputStream?.close()
        }

    }

    //查询图片
    //projection推断
    //selection选择
    fun searchPicture(context: Context): List<Bitmap> {
        val list = mutableListOf<Bitmap>()

        //设置查询条件
        val selection = MediaStore.Images.Media.DISPLAY_NAME + "=?"
        val arguments = arrayOf("sun03.jpg")
        //文件路径
        var filePath: String
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) {
            //分区存储
            val cursor = context.contentResolver.query(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                null,
                selection,
                arguments,
                null
            )
            cursor?.apply {
                while (moveToNext()) {
                    filePath = getString(getColumnIndexOrThrow(MediaStore.Images.Media.DATA))
                    list.add(BitmapFactory.decodeFile(filePath))
                }
            }
            cursor?.close()

        } else {
            //未开启分区存储
            filePath =
                Environment.getExternalStorageDirectory().absolutePath + File.separator + Environment.DIRECTORY_PICTURES + File.separator + "WZYPictures" + File.separator + "sun03.jpg"
            list.add(BitmapFactory.decodeFile(filePath))
        }

        return list
    }

    //删除图片
    fun deletePicture(context: Context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) {
            //分区存储
            //先要查询出Uri

            try {
                val selection = MediaStore.Images.Media.DISPLAY_NAME + "=?"
                val arguments = arrayOf("sun06.jpg")
                val cursor = context.contentResolver.query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    null,
                    selection,
                    arguments,
                    null
                )
                LogUtil.storageClean()
                cursor?.apply {
                    while (moveToNext()) {
                        val id = getLong(getColumnIndexOrThrow(MediaStore.Images.Media._ID))
                        LogUtil.default01(id.toString())
                        val uri =
                            ContentUris.withAppendedId(
                                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                id
                            )
                        val row = context.contentResolver.delete(uri, null, null)
                        LogUtil.storage("哈哈哈哈哈")
                        if (row > 0) {
                            LogUtil.storage("成功删除")
                        } else {
                            LogUtil.storage("失败删除")
                        }
                    }
                }
                cursor?.close()
            } catch (e: Exception) {
                LogUtil.default02(e.message.toString())
            }
        }
    }


}
